package cx3k.scheduler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;


import cx3k.GUI.ScheduledCourseGroup;
import cx3k.dataaccess.DataAccessService;
import cx3k.dataaccess.StandardDataAccessService;
import cx3k.dataaccess.exceptions.DataAccessException;
import cx3k.scheduler.objects.*;
import cx3k.scheduler.objects.util.CompareUtil;
import cx3k.scheduler.wrappers.CourseWrapper;

/**
 * Scheduler class that connects the GUI to the schedule objects,and gives the
 * GUI functionality to edit profile, generate schedules, add remove swap classes
 * , and edit schedules.... 
 */

public class Scheduler {	
	
	
	
	private Major profileMajor;
	private Minor profileMinor;
	private Major profileGFR;
	
	private String[] degreePrograms;
	private String[] majorStrings;
	private String[] minorStrings;
	
	private Map<Integer,Major> majors;
	private Map<Integer,Minor> minors;
	
	private static int maxSchedules = 9;
	private int scheduleCtr;
	private int[] scheduleIDs;
	private ArrayList<SchedulerCourse> coursesAddedtoSchedule;
	private Profile profile;
	private Map<Integer,Schedule> schedules;
	private DataAccessService das;
	
	private PriorityQueue<SchedulerCourse> required;
	private PriorityQueue<SchedulerCourse> gfrs;

	/**
	 * @param das - DataAccess service object
	 */
	Scheduler(DataAccessService das){
		this.das = das;
	}	
	/**
	 * Constructor for the scheduler, It attempts to connect to the database, then
	 * attempts to load all the majors and minors from the databse.
	 */
	public Scheduler(){
		this.das = StandardDataAccessService.getStandardDataAccessService();
		this.schedules = new HashMap<Integer, Schedule>();
		this.scheduleIDs = new int[10];
		
		getMajors();
		getMinors();
	}
	
	/**
	 * Loads the list of schedules from a specified file path.
	 * @param filePath - file path of the schedule to load.
	 */
	public int[] loadSchedules(String filePath){	
		
		das.setScheduleLocation(filePath);				
		try {schedules = das.getSchedules();}		
		catch (DataAccessException e) {
			//TODO - 
		}			
		int ctr = 0;
		int profileId = 0;		
		
		for(Schedule mySchedule : schedules.values()){
			if(ctr == 0)profileId = mySchedule.getScheduleProfile();
			this.scheduleIDs[ctr++] = mySchedule.getScheduleId();
		}
		
		loadProfile(profileId);		
		return scheduleIDs;
		
	}
	/**
	 * This method loads the profile of the student using the
	 * specific profile Id in the database, then it loads the profile
	 * major and minor;
	 * @param profileId - the profile ID of the student
	 * you wish to access.
	 */
	public void loadProfile(int profileId){		
		try {
				profile = das.getProfile(profileId);
				
				
				if (profile.getMajors() != null) for(Major myMajor: profile.getMajors()) {
					profileMajor = myMajor;
				}
				if (profile.getMinors() != null)for(Minor myMinor: profile.getMinors())profileMinor = myMinor;		
			
				
			} 
		catch (DataAccessException e){
			//TODO -
		}	
		//profile.setCompletedCourses(new HashSet<CompletedCourse>(10));
		try {
			das.saveProfile(profile);
		}
		catch (Exception e) {
			System.out.println("Couldn't save profile");
		}
		
	}	

	/**
	 * This function creates a new profile in the database and returns
	 * its corresponding id number as an integer.  **Note** You should call
	 * loadProfile with the profile id provided after running this function.
	 * 
	 * @param name - the name attached to the profile.
	 * @return - returns the profile id number used to access the database.
	 */
	public int newProfile(String name){
		Profile profile = new Profile();		
		profile.setName(name);		
		this.profile = profile;
		this.profileMajor = null;
		this.profileMinor = null;
		return profile.getProfileId();
	}
	/**
	 * Creates a new Schedule object with the provided name and returns,
	 * the unique corresponding id number of the schedule object.
	 *  
	 * @param name - name of the schedule to be created.
	 * @return - returns the schedule id of the schedule object just created,
	 * If max schedules have been created retunrs null.
	 */
	public Schedule newSchedule(String name){
		
		if(scheduleCtr == maxSchedules) return null;
		scheduleCtr++;
		scheduleIDs[scheduleCtr] =  scheduleCtr;		
		Schedule mySchedule = new Schedule();
		
		mySchedule.setScheduleId(scheduleCtr);
		mySchedule.setScheduleProfile(this.profile.getProfileId());
		mySchedule.setScheduleName(name);
		
		schedules.put(scheduleCtr, mySchedule);
		
		return mySchedule;						      					
	}
	
	/**
	 * This function returns a reference to all the
	 * schedules of a profile after loadSchedules method
	 * has been called.
	 * 
	 * @return - returns the list of schedules.
	 */
	public Map<Integer, Schedule> getSchedules(){		
		return schedules;		
	}

	/**
	 * @return - returns the current profile of the profile
	 * being managed by the scheduler
	 */
	public Profile getProfile(){
			return profile;
	}	
	
	
	/**
	* This function is for testing purposes. It basically only 
	* saves the current profile loaded
	*/
	public void saveProfile()
	{
		try {
			das.saveProfile(profile);
		} catch (DataAccessException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * This function returns an ArrayList of Course objects
	 * that are in the completed course list of the profile
	 * on the database.
	 * @return - ArrayList of courses from database.
	 */
	public ArrayList<Course> getCompletedClasses()
	{
		ArrayList<Course> myCs = new ArrayList<Course>();
		
		Set<CompletedCourse> myCourses = profile.getCompletedCourses();
		
		for(CompletedCourse myCourse: myCourses)
			myCs.add(myCourse.getCourse());			
		
		return myCs;
	}
	

	/**
	 * This method returns a list of majors in string
	 * format. (Should only be called once) 
	 * @return - a list of strings of majors
	 */
	private void getMajors(){
		
		majors = das.getAllMajors();	
		
		
		String[] myStrings = new String[majors.size()];
		int ctr = 0;
		
		for(Major myMajor: majors.values()){
			
			System.out.println("Major Loaded: " + myMajor.getProgram());
			if(myMajor.getProgram().equals("GFR")){profileGFR = myMajor;System.out.println("GFR LOADED");}
			myStrings[ctr++] = myMajor.getDescription();
			
		}		
		majorStrings = myStrings;
	}
	
	
	
	/**
	 * This method returns a list of minors in string
	 * format. (Should only be called once) 
	 * @return - a list of strings of minors
	 */
	private void getMinors(){
		minors = das.getAllMinors();
		String[] myStrings = new String[minors.size()];
		int ctr = 0;
		
		for(Minor myMinor: minors.values()){
			myStrings[ctr++] = myMinor.getDescription();
		}
		
		minorStrings = myStrings;
	}
	
	/**
	 * This function saves the profile to the database
	 * and the schedule to the xml file given a specific
	 * filepath.
	 */	
	public void save(String filepath){
		das.setScheduleLocation(filepath);
		this.saveSchedule(filepath);
		this.save();		
	}
	
	/**
	 * Returns a specific schedule object based on
	 * a given schedule id number.
	 */
	public Schedule getSchedule(int ScheduleId){		
		if(ScheduleId == 0)return null;		
		return schedules.get(ScheduleId);
	}

	/**
	 * Saves all schedules to the xml file given a specific
	 * filepath.
	 */
	public void saveSchedule(String filepath) {
		
		ArrayList<Schedule> temp = new ArrayList<Schedule>();		
		for(Schedule tempSchedule: schedules.values()){
			temp.add(tempSchedule);
		}
		//try {das.saveSchedules(temp);}
		//catch (DataAccessException e){
			//TODO - 
		//}	
	}
	
	/**
	 * Returns the array of scheduleIDs, if a schedule id is 0
	 * it is uninitialized.
	 */
	public int[] getScheduleIDs() {
		return scheduleIDs;
	}

	/**
	 * Saves the profile and all schedules to the database
	 * and local machine.
	 */
	public void save() {		
		try {das.saveProfile(profile);}
		catch (DataAccessException e) {
			//TODO -
		}		
		try {das.saveSchedules(schedules.values());}
		catch (DataAccessException e) {
			//TODO -
		}		
	}
 
	/**
	 * Simple getter function that retunrs all
	 * DegreePrograms strings represented in the databse.
	 * @return - String[]
	 */
	public String[] getDegreeProgramStrings(){
		return degreePrograms;
	}
	/**
	 * Simple getter function that returns all
	 * major strings represented in the databse.
	 * @return - String[]
	 */
	public String[] getMajorStrings() {
		return majorStrings;
	}
	/**
	 * Simple getter function that returns all
	 * minor strings represented in the database.
	 * @return - String[]
	 */
	public String[] getMinorStrings() {
		return minorStrings;
	}

	/**
	 * This function returns the int index of the minor in the array of
	 * minor strings;
	 * @return - int index of minor.
	 */
	public int getMinor(){			
		if (profileMinor == null) {
			return -1;
		}
		for(int x = 0; x < minorStrings.length; x++)
			if(minors.get(x).getDescription().equals(profileMinor.getDescription()))
				return x;		
		return -1;
	}
	
	
	/**
	 * This function returns the int index of the minor in the array of
	 * major strings;
	 * @return - int index of major.
	 */
	public int getMajor() {
		if (profileMajor == null) {
			return -1;
		}		
		int i = 0;
		for (Major m: majors.values()) {
			if (m.getDescription().equals(profileMajor.getDescription())) return i;
			i++;
		}			
		return -1;
	}	
	
	/**
	 * This function return the current name of the
	 * profile
	 * @return - Name of the profile in String form.
	 */
	public String getName() {
		return profile.getName();
	}
	
	
	/**
	 * Clears out all majors in the current profile and updates
	 * the current profiles major based on a given integer value.
	 */
	public void updateMajor(int Major) {
		profile.getMajors().clear();	
		Major myMajor = majors.get(Major);
		profileMajor = myMajor;
		Set<Major> myMajors = profile.getMajors();
		myMajors.add(myMajor);
	}
	
	/**
	 * Clears out all majors in the current profile and updates
	 * the current profiles major based on a given integer value.
	 */
	public void updateMinor(int Minor) {
		
		profile.getMinors().clear();		
		Minor myMinor = minors.get(Minor);
		profileMinor = myMinor;
		Set<Minor> myMajors = profile.getMinors();
		myMajors.add(myMinor);
	}
	
	/**
	 * Updates the name of the current profile user.
	 */
	public void updateName(String name) {
		profile.setName(name);		
	}

	/**
	 * Clears all completed courses from the set,
	 * used for testing purposes.
	 */
	public void clearCompletedClasses(){
		Set<CompletedCourse> mySet = profile.getCompletedCourses();
		mySet.clear();
		profile.setCompletedCourses(mySet);
	}
	
	/**
	 * Adds a course to the database once save has been called;
	 */
	public void addCompletedClass(Course c) {
		
		CompletedCourse myCourse = new CompletedCourse(profile,c);
		
		Set<CompletedCourse> myCompletedCourses = profile.getCompletedCourses();
		myCompletedCourses.add(myCourse);
		
		//for (CompletedCourse cc: myCompletedCourses) {
			//System.out.println(cc.getCourse().getName());
		//}
		
		profile.setCompletedCourses(myCompletedCourses);			
	}
	
	/**
	 * Removes a course from the database set once save has been called
	 */
	public void removeCompletedClass(Course c) {


		Set<CompletedCourse> newCompletedCourses = new HashSet<CompletedCourse>();
		Set<CompletedCourse> myCompletedCourses;

		//make deep copy
		for(CompletedCourse myCompletedCourse: profile.getCompletedCourses())
			newCompletedCourses.add(myCompletedCourse);
	
		//get reference and clear it
		myCompletedCourses = profile.getCompletedCourses();
		myCompletedCourses.clear();

		//iterate through deep copy adding each course back not adding the course removed
		for(CompletedCourse myCompletedCourse : newCompletedCourses){
			
			Course myCcourse = myCompletedCourse.getCourse();			
			if(!CompareUtil.Equals(c, myCcourse))myCompletedCourses.add(myCompletedCourse);			
			else System.out.println("Course Removed:" + myCcourse.getName());
			
		}	
		
		//set new completed course
		profile.setCompletedCourses(myCompletedCourses);
	}
	
	
	/**
	 * 
	 * @param scheduleID
	 * @param group1
	 * @return
	 */
	public int removeClass(int scheduleID,ScheduledCourseGroup group1) {
		
		
		ArrayList<Course> myCoursesCompare;
		ArrayList<Course> myCoursesRemove = group1.getCoursesList();
		Course tempCourse = myCoursesRemove.get(0);
		
		boolean found = false;
		
		Schedule mySchedule = getSchedule(scheduleID);
		
		if(mySchedule != null){
			for(AcademicYear myYear: mySchedule.getAcademicYears())	{	
				for(ScheduledSemester mySemester: myYear.getSemesters()){					
					for(ScheduledCourseGroup myCourseGroup : mySemester.getCourseGroups())
					{
						myCoursesCompare = myCourseGroup.getCoursesList();
						
						//must be same size
						if(myCoursesCompare.size() == myCoursesRemove.size()){							
							for(Course myCourse: myCoursesCompare)
								if(CompareUtil.Equals(myCourse, tempCourse))
									found = true;						
						}
						
						if(found)mySemester.removeCourse(myCourseGroup);
					}					
				}
			}
		}
		if(found == true)return 0;
		else return -1;
	}
	
	/**
	 * This function adds a class to a specific schedule, it requires a scheduleID, year number,
	 * term, and a ScheduledCourseGroup.
	 * 
	 * @param scheduleID - int, schedule id of the schedule you wish to edit.
	 * @param yearNumb - String, the year in wish you need to edit in the schedule.
	 * @param term - String, the term in wish you need to edit in the schedule.
	 * @param group1 - the actual course group object you want to add to the schedule.
	 * @return - returns 0 if the course was successfully added other wise returns -1.
	 */
	public int addClass(int scheduleID,String yearNumb,String term,ScheduledCourseGroup group1) {
		
		
		boolean found = false;
		Schedule mySchedule = getSchedule(scheduleID);
		
		if(mySchedule != null)
		{
			for(AcademicYear myYear: mySchedule.getAcademicYears())
			{
				if(myYear.getYearNumber() == yearNumb){
					
					for(ScheduledSemester mySemester: myYear.getSemesters()){					
						if(mySemester.getTerm() == term){
							found = true;
							mySemester.addCourse(group1);					
						}						
					}
				}
			}
		}

		if(found == true)return 0;
		else return -1;
	}
	
	
	/**
	 * This function swaps classes given 2 scheduled course group objects and a schedule id.
	 * @param scheduleID - the schedule in which you want to actually swap classes.
	 * @param group1 - a class in which you want to swap.
	 * @param group2 - another class in which you want to swap.
	 * @return - returns an int, -1 if it failed to find the courses to swap, otherwise 0.
	 */
	public int swapclass(int scheduleID, ScheduledCourseGroup group1,ScheduledCourseGroup group2) {
		
		Schedule mySchedule = getSchedule(scheduleID);
		ScheduledSemester mySem1 = null;
		ScheduledSemester mySem2 = null;
		
		if(mySchedule != null)
		{
			for(AcademicYear myYear: mySchedule.getAcademicYears())
			{
				for(ScheduledSemester mySemester: myYear.getSemesters())
				{
					if(mySemester.getGroups().contains(group1))mySem1 = mySemester;
					if(mySemester.getGroups().contains(group2))mySem2 = mySemester;					
				}
			}
		}
		
		if(mySem1 == null || mySem2 == null)return -1;
		
		mySem1.removeCourse(group1);
		mySem2.removeCourse(group2);
		
		mySem1.addCourse(group2);
		mySem2.addCourse(group1);	
		
		return 0;	
	}	
	
	/**
	 * Returns A schedule object with a four year plan.
	 * 
	 * @param startYear - should be a string representation of a year
	 * such as "2009".
	 * @param name - whatever you wish to name your schedule.
	 * @return - returns a generated schedule, if max schedules have 
	 * reached then will return a null schedule object;
	 */
	public Schedule generateSchedule(int intStartYear, String name) {
		
		//attempt to make a new schedule		
		int scheduleId = newSchedule(name).getScheduleId();
		
		if(scheduleId == -1 || profileMajor == null)return null;		
		
		//create or clear cousesaddedtoschedule ArrayList
		coursesAddedtoSchedule = new ArrayList<SchedulerCourse>();		
		Schedule mySchedule = schedules.get(scheduleId);	
		
		//initialize required,gfr arraylists, setup prereqs, prorities etc.....
		initializeMajor(profileMajor,profileGFR,profileMinor); 
		
		//should be while(!required.isEmpty())
		for(int x = 0; x < 4; x++){			
			AcademicYear myYear;
			String strStartYear1 = Integer.toString(intStartYear+x);
			myYear = generateYear(strStartYear1,intStartYear+x,14);		// <-- 14 is the number of max credits per semester
			mySchedule.addAcademicYear(myYear);			
		}
		
		schedules.put(scheduleId, mySchedule);
		
		Schedule.printSchedule(mySchedule);
		return mySchedule;
	}
	
//-------------------------------Private Functions------------------------------------------------

	private AcademicYear generateYear(String name, int year1, int maxCredits)
	{
		int year2 = year1 + 1;
		
		ScheduledSemester mySemester1;
		ScheduledSemester mySemester2;
		
		String myTerm1 = Integer.toString(year1);
		String myTerm2 = Integer.toString(year2);
		
		AcademicYear myAcademicYear = new AcademicYear(year1,year2);		
		
		mySemester1 = generateSemester(myTerm1,year1,maxCredits,0);			//shouldn't be null
		mySemester2 = generateSemester(myTerm2,year2,maxCredits,1);	
		
		myAcademicYear.addSemester(mySemester1);
		myAcademicYear.addSemester(mySemester2);
		
		return myAcademicYear;
	}

	private ScheduledSemester generateSemester(String term, int year, int maxCredits,int sessionType){		
		
		String sessionTypes[] = {"FA","SP"};		
		ScheduledSemester mySemester = new ScheduledSemester();
		
		ArrayList<SchedulerCourse> myCourses;
		ArrayList<ScheduledCourseGroup> mySchedCourseGroup;
		
		myCourses = createSemester(maxCredits);		
		ScheduledSemester myNewSemester = new ScheduledSemester();
		
		myNewSemester.setYear(year);
		myNewSemester.setTerm(term);
		
		if(sessionType == 0){
			mySemester.setTerm("Fall");
			mySchedCourseGroup = getScheduledCourseGroup(myCourses,year,sessionTypes[0]);
		}
		else {
			mySemester.setTerm("Spring");
			mySchedCourseGroup = getScheduledCourseGroup(myCourses,year,sessionTypes[1]);
		}
		
		mySemester.setCourses(mySchedCourseGroup);
				
		return mySemester;
	}	

	private ArrayList<ScheduledCourseGroup> getScheduledCourseGroup(ArrayList<SchedulerCourse> mySemester, 
			int term,
			String sessionType){		
		
		Semester semester = new Semester();
		semester.setYear(term);							
		semester.setSessionType(sessionType);			
		
		String courseNum;
		String degreeProgram;
		
		ArrayList<ScheduledCourseGroup> mySchedGroup = new ArrayList<ScheduledCourseGroup>();
		ArrayList<Course> myCourses;
		
		List<CourseWrapper> myTempC;		
		CourseWrapper myCW = null;
	
		for(SchedulerCourse myCourse: mySemester){
			
			myCourses = new ArrayList<Course>();
			
			for(int x = 0; x < myCourse.numOfClasses; x++){				
				
				courseNum = myCourse.courseNums[x];
				degreeProgram = myCourse.degreePrograms[x];					
				
				myTempC = CourseWrapper.getCourseWrappers(degreeProgram,courseNum,semester);
	
				if(myTempC.size() >= 1)myCW = myTempC.get(0);
				
				//Found a course in the database
				if(myCW != null){					
					myCourses.add(myCW.getCourse());				
				}
				//Create a non existing course not in the database
				//and set courseId to negative one.
				else {
					Course myNewCourse = new Course();
					myNewCourse.setCourseId(-1);
					myNewCourse.setCourseNum(courseNum);
					myNewCourse.setCredits(Integer.toString(myCourse.credits[x]));
					myNewCourse.setDegreeProgram(degreeProgram);
					myNewCourse.setDescription("Times for this class is not available");
					myCourses.add(myNewCourse);
				}				
				myCW = null;
			}
			
			ScheduledCourseGroup myNewCourseGroup = new ScheduledCourseGroup(myCourses);
			mySchedGroup.add(myNewCourseGroup);
		}
		return mySchedGroup;
	}
	
	private void initializeMajor(Major major ,Major gfr ,Minor minor)
	{
		required = new PriorityQueue<SchedulerCourse>();
		gfrs = new PriorityQueue<SchedulerCourse>();		
		
		ArrayList<SchedulerCourse> requiredArray = new ArrayList<SchedulerCourse>();
		ArrayList<SchedulerCourse> gfrArray = new ArrayList<SchedulerCourse>();
		
		//CreateDummyData myDummy = new CreateDummyData();									//test
		
		Set<RequirementGrouping> requiredGroup = null;
		Set<RequirementGrouping> minorGroup = null;
		Set<RequirementGrouping> GFRgroup = null;
		
		//Gets requirement groupings from database based on a specific major
		if(major != null)requiredGroup = major.getRequirementGroupings();	
		if(minor != null)minorGroup = minor.getRequirementGroupings(); 		
		if(gfr != null)GFRgroup = gfr.getRequirementGroupings(); 			
		
		//requiredGroup = myDummy.compSciRequirementGrouping(); 							//test
		//GFRgroup = myDummy.GFRRequirementGrouping();		  								//test
		
		//Separates required courses and GFR courses  need to create multiples based on credits required
		if(requiredGroup != null)for(RequirementGrouping myReqGrouping: requiredGroup)
		{			
			SchedulerCourse schedulerCourse = new SchedulerCourse(myReqGrouping);
			for(int x=0; x < schedulerCourse.numOfCoursesRequired; x++)requiredArray.add(schedulerCourse);		
		}
		//minor
		if(minorGroup != null)for(RequirementGrouping myReqGrouping: minorGroup)
		{			
			SchedulerCourse schedulerCourse = new SchedulerCourse(myReqGrouping);
			for(int x=0; x < schedulerCourse.numOfCoursesRequired; x++)requiredArray.add(schedulerCourse);		
		}
		//GFRs
		if(GFRgroup != null)for(RequirementGrouping myGFRGrouping: GFRgroup)
		{
			SchedulerCourse schedulerCourse = new SchedulerCourse(myGFRGrouping);
			for(int x=0; x < schedulerCourse.numOfCoursesRequired; x++)gfrArray.add(schedulerCourse);
		}		
				
		//setup full prerequisites-------------------------------
		for(SchedulerCourse myCourse: requiredArray)
			starterPrereq(myCourse);		
		
		for(SchedulerCourse myCourse: gfrArray)
			starterPrereq(myCourse);
		
		//setup priorities----------------------------------------
		for(SchedulerCourse myCourse: requiredArray){
			
			myCourse.priority = setUpPriority(myCourse,requiredArray,gfrArray);
			
			//Gives more priority to lower level classes (an attempt at making a more even schedule)
			if(!myCourse.isGrouping){
				if(myCourse.courseNum.charAt(0) == '1')myCourse.priority = myCourse.priority + 3;
				else if(myCourse.courseNum.charAt(0) == '2')myCourse.priority = myCourse.priority + 2;
				else if(myCourse.courseNum.charAt(0) == '3')myCourse.priority = myCourse.priority + 1;
			}			
		}	
		
		for(SchedulerCourse myCourse: gfrArray){			
			myCourse.priority = setUpPriority(myCourse,requiredArray,gfrArray);
			
			//Gives more priority to lower level classes (an attempt at making a more even schedule)
			if(!myCourse.isGrouping){
				if(myCourse.courseNum.charAt(0) == '1')myCourse.priority = myCourse.priority + 3;
				else if(myCourse.courseNum.charAt(0) == '2')myCourse.priority = myCourse.priority + 2;
				else if(myCourse.courseNum.charAt(0) == '3')myCourse.priority = myCourse.priority + 1;
			}	
		}
				
		//add to priority queues based on priorities-----------------------
		for(SchedulerCourse myCourse: requiredArray){
			System.out.println(myCourse);
			required.add(myCourse);
		}		
		for(SchedulerCourse myCourse: gfrArray){
			System.out.println(myCourse);
			gfrs.add(myCourse);
		}
		
	}	
	
	private ArrayList<SchedulerCourse> createSemester(int maxCredits)
	{	
		int creditC = 0;
		
		ArrayList<SchedulerCourse> newSemester = new ArrayList<SchedulerCourse>();		
		
		SchedulerCourse myCourseReq = null;
		SchedulerCourse myCourseGfr;
		
		ArrayList<SchedulerCourse> gfrAddBackToQueue = new ArrayList<SchedulerCourse>();
		ArrayList<SchedulerCourse> requiredAddBackToQueue = new ArrayList<SchedulerCourse>();
		ArrayList<SchedulerCourse> coursesCurrentlyAdded = new ArrayList<SchedulerCourse>();
		
		boolean addReqB = false;
		boolean addGfrB = false;
		
		while(creditC < maxCredits && !(required.isEmpty() && gfrs.isEmpty()))
		{
			addReqB = true;
			addGfrB = true;			

			if(!required.isEmpty())myCourseReq = required.remove();
			else myCourseReq = null;
									
			if(meetsPrereq(myCourseReq) && (creditC + myCourseReq.credit) <= maxCredits)
			{
				addReqB = false;
				newSemester.add(myCourseReq);
				coursesCurrentlyAdded.add(myCourseReq);
				creditC = creditC + myCourseReq.credit;
			}
			
			
			if(!gfrs.isEmpty())myCourseGfr = gfrs.remove();
			else myCourseGfr = null;
			
			if(meetsPrereq(myCourseGfr) && (creditC + myCourseGfr.credit) <= maxCredits)
			{
				addGfrB = false;
				newSemester.add(myCourseGfr);
				coursesCurrentlyAdded.add(myCourseGfr);
				creditC = creditC + myCourseGfr.credit;
			}
				
			if(addReqB && myCourseReq != null)requiredAddBackToQueue.add(myCourseReq);
			if(addGfrB && myCourseGfr != null)gfrAddBackToQueue.add(myCourseGfr);			
		}
		
		//add all required courses back to the queue
		for(int x = 0; x < requiredAddBackToQueue.size(); x++)
			required.add(requiredAddBackToQueue.get(x));
		
		//add all the gfr courses back to the queue
		for(int x = 0; x < gfrAddBackToQueue.size(); x++)
			gfrs.add(gfrAddBackToQueue.get(x));
		
		//add all the courses currently enrolled in to courses added to the schedule
		for(int x = 0; x < coursesCurrentlyAdded.size(); x++)
			coursesAddedtoSchedule.add(coursesCurrentlyAdded.get(x));
		
		return newSemester;
	}	
	
	private boolean meetsPrereq(SchedulerCourse myCourse){
		
		SchedulerCourse preReq;
		
		if(myCourse == null)return false;
		else if(myCourse.preReqs.size() == 0)return true;		
		else
		{
			for(int x = 0; x < myCourse.preReqs.size(); x++)
			{
				preReq = myCourse.preReqs.get(x);
				if(!coursesAddedtoSchedule.contains(preReq))return false;
			}
		}
		return true;
	}
		
	private int setUpPriority(SchedulerCourse myCourse, ArrayList<SchedulerCourse> required, 
			ArrayList<SchedulerCourse> gfr)	{
		
		int counter = 0;
		
		for(int x = 0; x < required.size(); x++){
			if(required.get(x).preReqs.contains(myCourse))
				counter++;
		}
		for(int x = 0; x < gfr.size(); x++){
			if(gfr.get(x).preReqs.contains(myCourse))
				counter++;
		}		
		return counter;
	}
	
	private void starterPrereq(SchedulerCourse myCourse)
	{		
		myCourse.preReqs = getNewPreReq(myCourse,myCourse.preReqs);		
	}
	
	private ArrayList<SchedulerCourse> getNewPreReq(SchedulerCourse myCourse, ArrayList<SchedulerCourse> myArray)
	{	
		SchedulerCourse myPrereq;	
		
		for(int x = 0; x < myCourse.preReqs.size(); x++)
		{
			myPrereq = myCourse.preReqs.get(x);
			if(!myArray.contains(myPrereq))myArray.add(myPrereq);			
			getNewPreReq(myPrereq, myArray);
		}		
		return myArray;
	}
}
	
